package com.clx;

import com.clx.channelHandler.hander.CrpcRequestDecoder;
import com.clx.channelHandler.hander.CrpcResponseEncoder;
import com.clx.channelHandler.hander.MethodCallHander;
import com.clx.core.HeartbeatDetector;
import com.clx.discovery.Registry;
import com.clx.discovery.RegistryConfig;
import com.clx.loadbalancer.LoadBalancer;
import com.clx.loadbalancer.impl.ConsistenceHashBalancer;
import com.clx.loadbalancer.impl.MinmumResponseTimeLoadBalancer;
import com.clx.loadbalancer.impl.RoundRobinLoadBalancer;
import com.clx.transport.message.CrpcRequest;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;


import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class CrpcBootstrap {

    public static final int PORT = 8090;
    // CrpcBootstrap是个单例，我们希望每个应用程序只有一个实例

    private static final CrpcBootstrap crpcBootstrap = new CrpcBootstrap();

    // 定义相关的一些基础配置
    private String appName = "default";
    private RegistryConfig registryConfig;
    private ProtocalConfig protocalConfig;
    public static final IdGenerator ID_GENERATOR = new IdGenerator(1,2);
    public static String SERIALIZE_TYPE = "jdk";
    public static String COMPRSS_TYPE = "gzip";

    public static final ThreadLocal<CrpcRequest> REQUEST_THREAD_LOCAL = new ThreadLocal<>();
    //维护一个zooKeeper实例
//    private ZooKeeper zooKeeper;

    // 注册中心
    private Registry registry;
    public static LoadBalancer LOAD_BALANCER;
    //连接的缓存,如果使用InetSocketAddress这样的类做key，一定要看他有没有重写equals方法，和toString方法
    public final static Map<InetSocketAddress, Channel> CHANNEL_CACHE = new ConcurrentHashMap<>(16);
    public final static TreeMap<Long, Channel> ANSWER_TIME_CHANNEL_CACHE = new TreeMap<>();

    // 维护已经发布且暴露的服务列表 key -> interface的全限定名 value -> ServiceConfig
    public static final Map<String,ServiceConfig<?>> SERVERS_LIST = new HashMap<>(16);

    // 定义全局的对外挂起的 completableFuture
    public final static Map<Long, CompletableFuture<Object>> PENDING_REQUEST = new ConcurrentHashMap<>(128);

    public CrpcBootstrap() {

        //构造启动引导程序时需要做一些什么初始化的事
    }

    public static CrpcBootstrap getInstance() {

        return crpcBootstrap;
    }

    //用来定义当前应用的名字
    public CrpcBootstrap application(String appName) {
        this.appName = appName;
        return this;
    }

    // 用来配置一个注册中心（RegistryConfig）
    //this当前实例
    public CrpcBootstrap registry(RegistryConfig registryConfig) {

        //这里维护一个zookeeper实例，但是，如果这样写就会将zookeeper和当前工程耦合
        // 我们其实是更希望以后可以扩展更多种不同的实现

        // 尝试使用 registryConfig 获取一个注册中心，有点工厂设计模式的意思了
        this.registry = registryConfig.getRegistry();
        // todo 需要修改
        CrpcBootstrap.LOAD_BALANCER = new RoundRobinLoadBalancer();
        return this;
    }

    /**
     * 配置当前暴露的服务使用的协议、
     * ProtocalConfig 协议的封装
     * */

    public CrpcBootstrap protocol(ProtocalConfig protocalConfig) {
        this.protocalConfig = protocalConfig;
        if (log.isDebugEnabled()){
            log.debug("当前工程使用了: {}协议进行序列化",protocalConfig.toString());
        }
        return this;
    }

    /**
     *服务提供方的相关api
     *
     * */
    /**
     *
     * 发布服务，将接口，接口-》实现，注册到服务中心
     * service 封装的需要发布的服务
     * */
    public CrpcBootstrap publish(ServiceConfig<?> service) {
        // 我们抽象了注册中心的概念，使用注册中心的一个实现完成注册
        // 有人会想，此时此刻难道不是强耦合了
        registry.register(service);

        // 1.当服务调用方，通过接口，方法名，具体的方法参数列表发起调用，提供方这么知道使用哪一个实现
        // (1) new 一个 （2）spring beanFactory。getBean（Class） （3）自己维护映射关系
        SERVERS_LIST.put(service.getInterface().getName(),service);
        return this;
    }
    /***
     *
     * 批量发布
     * services 封装的需要发布的服务集合
     * this当前实例
     */
    public CrpcBootstrap publish(List<ServiceConfig<?>> services) {
        for (ServiceConfig<?> service : services){
            this.publish(service);
        }
        return this;
    }

    /**
     * 启动nettry服务
     * */
    public void start() {

        //1.创建eventloop，老板只负责处理请求，之后会将请求重新发送至worker
        // “老板”线程组：负责「接受客户端连接」
        EventLoopGroup boss = new NioEventLoopGroup(2);
        // “工人”线程组：负责「处理连接后的 I/O 操作」
        EventLoopGroup worker = new NioEventLoopGroup(10);
        try {
            //2需要一个服务器引导程序
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //3.配置服务器
            serverBootstrap = serverBootstrap
                    // 绑定“老板/工人”线程组
                    .group(boss, worker)
                    // 指定「服务端接受连接的通道类型」为 NIO 实现
                    .channel(NioServerSocketChannel.class)
                    // 配置「客户端连接通道的初始化逻辑
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                                      @Override
                                      protected void initChannel(SocketChannel socketChannel) throws Exception {
                                          // 是核心，我们需要添加很多入站和出站的handel
                                          // 向“客户端连接通道”的「处理器管道（Pipeline）」中，添加自定义处理器 MyChannelHandler
                                          // CrpcBootstrap.java
                                          socketChannel.pipeline().addLast(new LoggingHandler())
                                                  .addLast(new CrpcRequestDecoder())
                                                  // 根据请求进行方法调用
                                                  .addLast(new MethodCallHander())
                                                  .addLast(new CrpcResponseEncoder());
                                      }
                                  });
            //4.绑定端口，并「同步等待绑定完成」
            ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();


            //closeFuture().sync()：让服务端 “阻塞等待”，直到通道被关闭（比如程序停止时）
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                // 优雅关闭“老板/工人”线程组（等待已有任务完成，再释放资源）
                boss.shutdownGracefully().sync();
                worker.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        
    }

        /**
         *服务调用方的相关api
         *
         * */
        public CrpcBootstrap reference (ReferenceConfig<?> reference){

            // 开启对这个心跳的检测
            HeartbeatDetector.detectorHeartbeat(reference.getInterfaceRef().getName());

            // 在这个方法里我们是否可以拿到相关的配置项-注册中心
            // 配置reference，将来调用get方法是，方便生成代理对象
            // 1,reference需要一个注册中心
            reference.setRegistry(registry);
            return this;
        }

        /**
         * 配置序列化的方式
         *serializeType 序列化的方式
         * */
    public CrpcBootstrap serialize(String serializeType) {
        SERIALIZE_TYPE = serializeType;
        if (log.isDebugEnabled()){
            log.debug("我们配置了使用系列化的方式为【{}】",serializeType);
        }
        return this;
    }

    public CrpcBootstrap compress(String compressType) {
        COMPRSS_TYPE = compressType;
        if (log.isDebugEnabled()){
            log.debug("我们配置了使用的压缩算法为【{}】",compressType);
        }
        return this;
    }

    public Registry getRegistry() {
        return registry;
    }
}

